Skip to content

fix(desktop): explicit bot opens and session creates dial the pool at foreground priority - #113269

Merged
austinpickett merged 2 commits into
mainfrom
fix/desktop-foreground-bot-dials
Sep 16, 2026
Merged

austinpickett merged 2 commits into
mainfrom
fix/desktop-foreground-bot-dials

Conversation

@austinpickett

Copy link
Copy Markdown
Collaborator

Symptom

#105104: clicking a bot in the roster intermittently does nothing. No backend activity, then a "try again" toast. On the same 3-slot local pool, the first send on a fresh chat and "New session" / tab-strip "+" could wait out the background slot timeout while roster hydration held every slot.

What was happening, per layer

#102281 / #104139 tagged the activation doors (openGatewayForAgent, openGatewayForProfile, ensureGatewayForAgent, sharedPrimaryRoute) as foreground, and 5eb0ed4531 gave requestGatewayForProfile a { spawnPriority } options object. Two RPC paths never got it:

  • apps/desktop/src/store/gateway.ts:1026 requestGatewayForAgent and :1229 retainGatewayForAgent had no priority parameter. Their isAttachedSharedRemote probe, openSecondary connect, and both requestGatewayForProfile forwards dialed background. retainGatewayForAgent's plain-profile branch (gatewayForProfile(key, true)) too; neither salvaged PR covered that one.
  • apps/desktop/src/app/session/hooks/use-session-actions/index.ts:604,612 (createBackendSessionForSend) and :824,832 (openNewSessionTile) are the only callers of that pair for a session create, and they passed nothing.
  • apps/desktop/src/plugins/hermes-bots/canonical-chat.ts:245 requestForBot(bot, 'session.list') -> routing.ts:213 host.requestProfile(route, method, params) -> sdk/index.ts:249 requestGatewayForAgent(...). The click's first RPC, the one that cold-spawns the bot's backend, carried no intent. The later host.openSession is already foreground but never runs until this lookup returns.

Fix, per layer

  • gateway.ts: requestGatewayForAgent(..., timeoutMs?, signal?, { spawnPriority = 'background' } = {}) and retainGatewayForAgent(connectionId, profile, { spawnPriority = 'background' } = {}), same options shape as requestGatewayForProfile. Forwarded into both requestGatewayForProfile branches, isAttachedSharedRemote, openSecondary, and gatewayForProfile(key, true, spawnPriority). SpawnPriority is exported for the SDK.
  • use-session-actions/index.ts: the four create-path calls pass { spawnPriority: 'foreground' }; the retain is the first dial so it carries the tag too.
  • sdk/index.ts: host.requestProfile(route, method, params, timeoutMs?, options?: { spawnPriority }). timeoutMs stays positional so no existing caller changes shape, and the SDK keeps its exact call arity when options are absent (the pool tests pin that). JSDoc on requestProfile documents the choice.
  • hermes-bots/routing.ts: requestForBot(bot, method, params, options?) forwards spawnPriority only when set. canonical-chat.ts: the canonical session.list and the session.create on a first-ever open pass 'foreground'. Passive roster warming (profiles.list, ui_meta) still dials background.

Tests

Both polarities at each layer, converted from the PRs' change-detectors into behaviour contracts:

  • store/gateway-spawn-priority.test.ts: request/retain tagged -> every registry dial carries priority: 'foreground'; untagged -> no priority key; retain on the plain-profile (null connection) route dials foreground.
  • sdk/profile-routing.test.ts: options form with a timeout and without one; the existing numeric-timeout and no-timeout tests still pin the old arity.
  • plugins/hermes-bots/routing.test.ts: tag forwarded as the fifth arg; an untagged call keeps three args.
  • plugins/hermes-bots/canonical-chat-registry.test.ts: session.list and session.create carry the tag, session.title does not.
  • use-session-actions.test.tsx, default-new-session.test.tsx: create matchers pin the tagged dial and the tagged retain.

Red on base: with the five production files stashed and the tests kept, 9 failed / 187 passed across the five touched test files (all nine are the new foreground assertions; the negative-polarity tests pass on base as expected). Green after: 196/196. npx vitest run src/store src/sdk src/plugins/hermes-bots src/app/session: 3173 passed, the 2 failures are voice-prefs.test.ts, pre-existing on main. npm run typecheck clean; npx eslint on the touched files: 0 errors.

Not fixed here

Supersedes #110354, #105390.

Thanks @jxfjosh: the diagnosis that the canonical lookup's session.list is the click's first dial, and that it had to go through host.requestProfile, is the centre of the second commit. Thanks @nftpoetrist: the observation that requestGatewayForAgent / retainGatewayForAgent were the one untagged dial pair left after #102281, and that the retain must be tagged as well as the create, is the first commit. Both are credited as commit authors.

nftpoetrist and others added 2 commits September 16, 2026 14:04
…t/retainGatewayForAgent and the session-create dials

The #102281/#104139 chain tagged every activation-door dial
(openGatewayForAgent/openGatewayForProfile/ensureGatewayForAgent/
sharedPrimaryRoute) as 'foreground' so a user-initiated open takes the
pool's reserved slot instead of queuing behind background roster
hydration. requestGatewayForAgent and retainGatewayForAgent never got a
spawnPriority, so they always dialed 'background', and they are the only
dial path createBackendSessionForSend (first send on a fresh chat) and
openNewSessionTile ("New session" / tab-strip "+") use. On a saturated
3-slot local pool the user's first message or new-tab click could wait
out the background slot timeout.

Add `{ spawnPriority }` options (main's requestGatewayForProfile shape,
5eb0ed4) to both functions and forward it into every dial they make:
requestGatewayForProfile on the scope===key and primary-registry
branches, isAttachedSharedRemote, openSecondary, and the plain-profile
retain branch's gatewayForProfile(key, true, ...) which neither #105390
nor #110354 covered. The four session-create call sites pass
'foreground'.

Tests: gateway-spawn-priority.test.ts gains both polarities for
request/retain plus the plain-profile retain branch; the
use-session-actions and default-new-session matchers pin the tagged
dial. SpawnPriority is now exported for the SDK.
….requestProfile options

Clicking a bot in the roster runs findExistingCanonicalChat ->
requestForBot(bot, 'session.list') -> host.requestProfile(route, ...)
-> requestGatewayForAgent with no intent, so the first RPC of the
gesture, the one that cold-spawns the bot's backend on a local pool,
dialed 'background'. With three roster backends already hydrating the
click produced no backend activity and the fail-closed lookup surfaced
as a "try again" toast (#105104). The later host.openSession is already
foreground, but it never runs until this lookup returns.

host.requestProfile gains a fifth `options?: { spawnPriority }`
argument; `timeoutMs` stays the fourth positional so no existing caller
changes shape, and the SDK keeps its exact call arity when no options
are given. requestForBot takes the same options and forwards them only
when set, so passive roster warming (profiles.list, ui_meta) still
dials background. The canonical `session.list` and the `session.create`
that follows on a first-ever open both pass 'foreground'.

Tests: profile-routing.test.ts (options form with and without a
timeout), routing.test.ts (tag forwarded, untagged call keeps three
args), canonical-chat-registry.test.ts (session.list and session.create
carry foreground; session.title does not).
@github-actions

github-actions Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 338d833 — fix(desktop): canonical Bot Chat lookup dials foreground thr

debug info

CI timings

CI timings · View report · View job

Wall time 4m14s vs 6m43s (-37.0%). 2 job(s) slower, 3 faster, 1 unchanged.

  • Detect affected areas: -23.0s
  • Profile artifact check / Reject profile archives: -8.0s
  • Check no case-colliding filenames / check-case-collisions: +7.0s
  • Check no committed infographics / check-no-committed-infographics: +3.0s
  • JS & TS checks / JS & TS checks: -2.0s

@austinpickett
austinpickett merged commit a7691f4 into main Sep 16, 2026
28 checks passed
@austinpickett
austinpickett deleted the fix/desktop-foreground-bot-dials branch September 16, 2026 18:27
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/desktop Electron desktop app (apps/desktop/*) labels Sep 16, 2026
QuixThe2nd pushed a commit to QuixThe2nd/hermes-ide that referenced this pull request Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants